package org.iabako.server.serviceimpl.business;

import org.iabako.server.dao.business.ClientCommunicationDAO;
import org.iabako.server.dao.business.QueryDefinitionDAO;
import org.iabako.server.mail.IabakoMailHelper;
import org.iabako.server.mail.IabakoMailSender;
import org.iabako.server.serviceimpl.security.SessionUser;
import org.iabako.server.tools.BundleMessages;
import org.iabako.shared.entity.business.ClientCommunication;
import org.iabako.shared.entity.enumeration.IabakoPackage;
import org.iabako.shared.entity.enumeration.TrackingType;
import org.iabako.shared.entity.user.Enterprise;
import org.iabako.shared.entity.user.UserCredit;
import org.iabako.shared.exception.IabakoPackageForbiddenException;
import org.iabako.shared.iservice.business.ClientConversationService;
import org.iabako.shared.iservice.business.CommunicationService;
import org.iabako.shared.iservice.business.TrackingService;
import org.iabako.shared.iservice.user.UserService;
import org.iabako.shared.querydefinition.columndefinition.ClientColumnEnum;
import org.iabako.shared.querydefinition.dto.QueryResultDTO;
import org.iabako.shared.tools.GenericTools;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.EditorKit;
import javax.swing.text.html.HTMLEditorKit;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.*;

/**
 * Created by jose on 02/02/15.
 */

@Service("communicationService")
public class CommunicationServiceImpl extends AbstractEnMasseService implements CommunicationService {

    @Autowired
    UserService userService;
    @Autowired
    ClientConversationService clientConversationService;
    @Autowired
    QueryDefinitionDAO queryDefinitionDAO;
    @Autowired
    IabakoMailSender iabakoMailSender;
    @Autowired
    SessionUser sessionUser;
    @Autowired
    BundleMessages messages;
    @Autowired
    ClientCommunicationDAO clientCommunicationDAO;
    @Autowired
    TrackingService trackingService;

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void sendCommunication(Map<Long, QueryResultDTO> clients, String subject, String html) throws IabakoPackageForbiddenException {

        IabakoPackage iabakoPackage = sessionUser.getUser().getUserCredit().getIabakoPackage();

        Enterprise enterprise = sessionUser.getUser().getEnterprise().getParentEnterprise() != null ?
                sessionUser.getUser().getEnterprise().getParentEnterprise() : sessionUser.getUser().getEnterprise();

        UserCredit userCredit = userService.getPrincipalUser().getUserCredit();

        int commInBalance = enterprise.getEnterpriseBalance().getCommunicationTracks().size();
        int commNumberToSend = clients.size() / 50 + 1;
        int commLimit = userCredit.getCommunicationLimit();

        if (commNumberToSend + commInBalance > commLimit) {

            String packageWithoutPrice = messages.getLabel(iabakoPackage.getLabelKey());
            if (packageWithoutPrice.contains("(")) {
                packageWithoutPrice = packageWithoutPrice.substring(0, packageWithoutPrice.indexOf("("));
            }

            String message = iabakoPackage.isStarter() ?
                    "new_communication_forbidden_by_package_text_stater" : "new_communication_forbidden_by_package_text";

            String errorMessage = messages.getLabel(message,
                    commInBalance + "",
                    packageWithoutPrice.trim(),
                    commLimit + "",
                    commNumberToSend + "",
                    GenericTools.formatDate(userCredit.getExpirationDate()));

            throw new IabakoPackageForbiddenException("action_forbidden_by_package_title", errorMessage, "snifficon", false);
        }


        Map<ClientCommunication, List<Long>> clientCommunicationsMap = new HashMap<ClientCommunication, List<Long>>();

        ClientCommunication clientCommunication = null;
        String toListAsString = "";
        List<Long> clientsPerComm = null;
        int count = 0;

        for (Long idClient : clients.keySet()) {

            if (count == 0 || count == 50) {
                count = 0;
                clientCommunication = new ClientCommunication();
                toListAsString = "";
                clientsPerComm = new ArrayList<Long>();

                clientCommunication.setFrom(sessionUser.getUser().getEnterprise().getEmail());
                clientCommunication.setDate(new Date());
                clientCommunication.setSubject(subject);
                clientCommunication.setBody(html);

                clientCommunicationsMap.put(clientCommunication, clientsPerComm);
            }

            String email = (String) clients.get(idClient).getValues().get(ClientColumnEnum.email).getValue();
            clientCommunication.addToList(email);
            toListAsString += email + "; ";
            clientCommunication.setTo(toListAsString.length() > 3000 ? toListAsString.substring(0, 3000) : toListAsString);
            clientsPerComm.add(idClient);
            count++;
        }

        Map<ClientCommunication, List<Long>> clientCommunicationsMapAfterPersist = new HashMap<ClientCommunication, List<Long>>();

        for (ClientCommunication comm : clientCommunicationsMap.keySet()) {
            List<Long> idClientList = clientCommunicationsMap.get(comm);
            clientCommunicationDAO.persist(comm);
            clientCommunicationsMapAfterPersist.put(comm, idClientList);
        }

        //To avoid useless and expensive Client charging to feed @ManyToMany Client-ClientCommunication relation
        //We pass by this helpful function.
        clientCommunicationDAO.addClientCommunications(clientCommunicationsMapAfterPersist);

        for (ClientCommunication comm : clientCommunicationsMapAfterPersist.keySet()) {

            userService.addCommunicationToEnterpriseBalance(comm);

            IabakoMailHelper iabakoMailHelper = new IabakoMailHelper(null,
                    sessionUser.getUser().getEnterprise().getName(),
                    comm.getToList(), subject, html);

            iabakoMailHelper.setNoReplyEmail(true);
            iabakoMailHelper.setDisableIabakoLogo(true);
            iabakoMailHelper.setAddEnterpriseLogo(true);
            iabakoMailHelper.setDisableDefaultFooter(true);
            iabakoMailHelper.setEnablePoweredByFooter(true);

            if (iabakoMailSender.sendMail(iabakoMailHelper)) {

                String body = (iabakoMailHelper).getBody();
                body = getTextFromHtml(body);
                //Body in text mode
                iabakoMailHelper.setBody(body);

                for (Long idClient : clientCommunicationsMapAfterPersist.get(comm)) {
                    String email = (String) clients.get(idClient).getValues().get(ClientColumnEnum.email).getValue();
                    clientConversationService.addConversationFromEmail(idClient, email, iabakoMailHelper);
                }
            }
        }

        trackingService.addTrackingToUserSession(TrackingType.communicationCreate, null, clients.size(), getComment(subject, clients));
    }

    private String getComment(String subject, Map<Long, QueryResultDTO> clients) {
        String comment = messages.getLabel("tracking_communication_comment", subject, GenericTools.formatTime(new Date()));
        for (Long idClient : clients.keySet()) {
            String name = (String) clients.get(idClient).getValues().get(ClientColumnEnum.name).getValue();
            String email = (String) clients.get(idClient).getValues().get(ClientColumnEnum.email).getValue();
            comment += name + " (" + email + ")\n";
        }
        return comment + "\n";
    }

    private String getTextFromHtml(String body) {
        EditorKit kit = new HTMLEditorKit();
        Document doc = kit.createDefaultDocument();

        // The Document class does not yet handle charset's properly.
        doc.putProperty("IgnoreCharsetDirective", Boolean.TRUE);
        // Create a reader on the HTML content.
        body = body.replace("<br>", "__lineBreak__").replace("<br/>", "__lineBreak__").replace("</br>", "");
        Reader rd = new StringReader(body);

        try {
            // Parse the HTML.
            kit.read(rd, doc, 0);
            String text = doc.getText(0, doc.getLength());
            //delete first "\n" put systematically by getText() method
            text = text.substring("\n".length());
            text = text.replace("__lineBreak__", "\n");

            rd.close();

            return text;
        } catch (IOException e) {
            return null;
        } catch (BadLocationException e) {
            return null;
        }
    }
}
